package com.elitel.common.auth.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.elitel.common.auth.AuthProperties;
import com.elitel.common.core.constant.SysUserConstant;
import com.elitel.common.utils.HttpUtils;
import com.elitel.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * cas 认证过滤器
 * 覆盖 AuthenticationFilter
 *
 * @author hi_bj
 */
public class Oauth2Filter implements Filter {
    private AuthProperties authProperties;

    public void setAuthProperties(AuthProperties authProperties) {
        this.authProperties = authProperties;
    }

    private static final Logger logger = LoggerFactory.getLogger(Oauth2Filter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession(false);
        if (session == null) {
            logger.error("session 为空, 未登录");
        }
        // 这个字段如果有值的话 证明已经登录
        String verifyFieldValue = session == null ? null : (String) session.getAttribute(SysUserConstant.LOGIN_ACCOUNT);
        logger.info("session 获取到验证字段为：{}，其值为:{}", SysUserConstant.LOGIN_ACCOUNT, verifyFieldValue);
        if (this.isRequestUrlExcluded(request) || (verifyFieldValue != null && !verifyFieldValue.isEmpty())) {
            logger.info("当前请求地址无需拦截，或者验证已经通过");
            filterChain.doFilter(request, response);
            return;
        }

        if (isLogin(request, response)) {
            logger.info("用户已经登录");
            filterChain.doFilter(request, response);
            return;
        }

        //接口响应 401 ，以及重定向地址
        String xRequestedWith = request.getHeader("X-Requested-With");
        String urlToRedirectTo = authProperties.getCenter().getServerLoginUrl();
        if ("XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
            logger.info("redirecting to \"{}\"", urlToRedirectTo);
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(JSON.toJSONString(get401RedirectMap(urlToRedirectTo)));
            out.flush();
            out.close();
        } else {
            logger.debug("redirecting to \"{}\"", urlToRedirectTo);
            response.sendRedirect(urlToRedirectTo);
        }

    }

    private boolean isLogin(HttpServletRequest request, HttpServletResponse response) {
        String code = request.getParameter("code");
        if (StringUtils.isEmpty(code)) {
            Object oauth2InfoObj = request.getSession(false) == null ? null : request.getSession(false).getAttribute(SysUserConstant.OAUTH2_INFO);
            if (oauth2InfoObj == null) {
                return false;
            }
            JSONObject oauth2InfoJson = JSONObject.parseObject(oauth2InfoObj.toString());
            Integer expiresIn = (Integer) oauth2InfoJson.get("expires_in");
            if (expiresIn != null && expiresIn > 0) {
                return true;
            } else {
                return refreshToken(oauth2InfoJson, request);
            }
        } else {
            return this.setAccessToken(request, code);
        }
    }

    private boolean refreshToken(JSONObject oauth2InfoJson, HttpServletRequest request) {
        // 刷新token
        String refreshToken = (String) oauth2InfoJson.get("refresh_token");
        String resStr = HttpUtils.sendGet(authProperties.getCenter().getRefreshTokenUrl() + refreshToken);
        JSONObject resJson = JSONObject.parseObject(resStr);
        Object o = resJson.get("access_token");
        if (StringUtils.isNotEmpty(o + "")) {
            resJson.put("refresh_token", refreshToken);
            // 此时不能创建会话，需要获取会话
            request.getSession(false).setAttribute(SysUserConstant.OAUTH2_INFO, resJson.toJSONString());
            // 获取用户信息
            return this.setUserInfo((String) resJson.get("access_token"), request);
        } else {
            return false;
        }
    }


    private boolean setAccessToken(HttpServletRequest request, String code) {
        String resStr = HttpUtils.sendGet(authProperties.getCenter().getAccessTokenUrl() + code);
        if (StringUtils.isNotEmpty(resStr)) {
            JSONObject resJson = JSONObject.parseObject(resStr);
            if (StringUtils.isNotEmpty(resJson.get("access_token") + "")) {
                // 此时要创建会话
                request.getSession(true).setAttribute(SysUserConstant.OAUTH2_INFO, resStr);
                // 获取并设置用户信息
                return this.setUserInfo((String) resJson.get("access_token"), request);
            } else {
                return false;
            }
        } else {
            return false;
        }
    }


    private boolean setUserInfo(String accessToken, HttpServletRequest request) {
        String userInfoStr = HttpUtils.sendGet(authProperties.getCenter().getUserInfoUrl() + accessToken);
        if (StringUtils.isNotEmpty(userInfoStr)) {
            JSONObject userInfoJson = JSONObject.parseObject(userInfoStr);
            Map attr = (Map) userInfoJson.get("attributes");
            request.getSession(false).setAttribute(SysUserConstant.LOGIN_ACCOUNT, attr.get("name"));
            return true;
        } else {
            return false;
        }
    }


    private boolean isRequestUrlExcluded(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        System.out.println("request.getRequestURI() = " + requestURI);
        // 获取contextPath
        String contextPath = request.getContextPath();
        requestURI = requestURI.replace(contextPath, "");
        String regExcludeUrl = authProperties.getRegExcludeUrl();
        return isUrlExcluded(regExcludeUrl, requestURI);
    }

    @Override
    public void destroy() {

    }


    public static boolean isUrlExcluded(String regExcludeUrl, String requestURI) {
        if (regExcludeUrl == null || regExcludeUrl.isEmpty() || requestURI == null) {
            return false;
        }
        // 分割正则表达式字符串为单独的模式
        String[] patterns = regExcludeUrl.split("\\|");

        for (String patternStr : patterns) {
            // 如果模式为空则跳过
            if (patternStr.isEmpty()) {
                continue;
            }

            // 创建Pattern对象，启用正则表达式匹配
            Pattern pattern = Pattern.compile(patternStr);
            Matcher matcher = pattern.matcher(requestURI);

            // 如果找到匹配则返回true
            if (matcher.find()) {
                return true;
            }
        }

        // 没有找到任何匹配
        return false;
    }


    public static Map get401RedirectMap(String urlToRedirectTo) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", 401);
        resultMap.put("success", false);
        resultMap.put("msg", AuthProperties.Type.OAUTH2 + "");
        HashMap<String, Object> data = new HashMap<>(1);
        data.put("url", urlToRedirectTo);
        resultMap.put("data", data);

        return resultMap;
    }


}
